<?php

class simulator
{
    public $attacker;
    public $defender;
    protected $winner = NULL;
    protected $turn = 0;

    public function __construct($attacker_hero, $defender_hero)
    {
        $this->attacker = $attacker_hero;
        $this->defender = $defender_hero;
    }

    public function get_winner()
    {
        return $this->winner;
    }

    public function run()
    {
        if ($this->attacker->is_dead() || $this->defender->is_dead())
        {
            $this->winner = $this->defender->is_dead() ? ($this->attacker->is_dead() ? NULL : 'attacker') : 'defender';
            return false;
        }
        $this->turn++;
        if ($this->turn > 200)
            die("infinite loop !!!");

        $this->attacker->magic_begin_hook($this->turn, $this->defender->army, $this->defender);
        $this->defender->magic_begin_hook($this->turn, $this->attacker->army, $this->attacker);

        $att_unit = $this->attacker->army->get_current_stack();
        $def_unit = $this->defender->army->get_current_stack();
        $att_total_pow = $this->get_stack_effective_power($att_unit, $this->attacker, $def_unit);
        $def_total_pow = $this->get_stack_effective_power($def_unit, $this->defender, $att_unit);
        $att_bonus_pow = $this->get_stack_bonus_power($att_unit, $this->attacker, $def_unit);
        $def_bonus_pow = $this->get_stack_bonus_power($def_unit, $this->defender, $att_unit);

        $att_remaining_unit = $this->get_remaining_units($att_total_pow, $def_total_pow, $att_unit);
        $def_remaining_unit = $this->get_remaining_units($def_total_pow, $att_total_pow, $def_unit);
        $att_overrun = ($att_bonus_pow >= $def_total_pow);
        $def_overrun = ($def_bonus_pow >= $att_total_pow);
        $att_unit_old_quantity = $att_unit->quantity;
        $def_unit_old_quantity = $def_unit->quantity;
        $this->attacker->army->update_remaining_units($att_remaining_unit);
        $this->defender->army->update_remaining_units($def_remaining_unit);
        return array('attacker' => array('before' => array('quantity' => $att_unit_old_quantity,
                                                           'power' => $att_total_pow),
                                         'after' => array('quantity' => $att_remaining_unit,
                                                          'power' => !$att_remaining_unit ? 0 :
                                                                     $this->get_stack_effective_power($this->attacker->army->get_current_stack(),
                                                                                                      $this->attacker,
                                                                                                      $def_unit)
                                                         ),
                                         'overrun' => $att_overrun,
                                         'type' => $att_unit->type,
                                         'name' => $att_unit->name
                                         ),
                      'defender' => array('before' => array('quantity' => $def_unit_old_quantity,
                                                            'power' => $def_total_pow),
                                          'after' => array('quantity' => $def_remaining_unit,
                                                           'power' => !$def_remaining_unit ? 0 :
                                                                     $this->get_stack_effective_power($this->defender->army->get_current_stack(),
                                                                                                      $this->defender,
                                                                                                      $att_unit)
                                                         ),
                                          'overrun' => $def_overrun,
                                          'type' => $def_unit->type,
                                          'name' => $def_unit->name
                                          )
                      );
    }

    protected function get_remaining_units($att_total_pow, $def_total_pow,
        unit $att_unit)
    {
        $att_remaining_unit = ceil(($att_total_pow - $def_total_pow) /
            $att_unit->power);
        if ($att_remaining_unit > $att_unit->quantity)
        {
            return $att_unit->quantity;
        }
        if ($att_remaining_unit < 0)
        {
            return 0;
        }
        return $att_remaining_unit;
    }

    protected function get_stack_effective_power(unit $att, hero $hero,
        unit $def)
    {
        $mult = 1 + $this->get_bonus_multiplicator($att, $hero, $def);
        $pow = $att->power * $mult;
        $pow += $this->get_bonus_adder($att, $hero, $def);
        return $pow * $att->quantity;
    }

    protected function get_stack_bonus_power(unit $att, hero $hero,
        unit $def)
    {
        $mult = $this->get_bonus_multiplicator($att, $hero, $def);
        $pow = $att->power * $mult;
        $pow += $this->get_bonus_adder($att, $hero, $def);
        return $pow * $att->quantity;
    }

    protected function get_bonus_multiplicator(unit $att, hero $hero,
        unit $def)
    {
        $mult = 0;
        $mult += $hero->get_level_bonus();
        $mult += $hero->get_tactician_bonus();
        $mult += $hero->get_unit_type_bonus($att->type);
        $mult += $att->get_attack_type_bonus($def->type);
        return $mult;
    }

    protected function get_bonus_adder(unit $att, hero $hero,
        unit $def)
    {
        $add = 0;
        $add += $hero->get_harrang_goodpay_bonus();
        return $add;
    }

    public function get_unit_type_bonus($attack_type, $defend_type)
    {
        if ($attack_type == ARCHER && $defend_type == INFANTRY ||
            $attack_type == INFANTRY && $defend_type == CAVALRY ||
            $attack_type == CAVALRY && $defend_type == ARCHER)
        {
            return 0.5;
        }
        return 0;
    }
}

?>
